package any.xpath;

import static any.Any.create;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.jaxen.BaseXPath;
import org.jaxen.Context;
import org.jaxen.JaxenException;
import org.jaxen.XPath;

import any.Any;
import any.ImmutableAny;
import any.SequentialAny;

/**
 * An XPath implementation for JavaBeans.
 * 
 * <p>
 * This is the main entry point for matching an XPath against a JavaBean tree.
 * You create a compiled XPath object, then match it against one or more context
 * nodes using the {@link #selectNodes(Object)} method, as in the following
 * example:
 * </p>
 * 
 * <pre>
 * Node node = ...;
 * XPath path = new AnythingXPath(&quot;a/b/c&quot;);
 * List results = path.selectNodes(node);
 * </pre>
 * 
 * @see BaseXPath
 */
public class AnythingXPath extends BaseXPath {

	private static final long serialVersionUID = -1567521943360266313L;

	/**
	 * Construct given an XPath expression string.
	 * 
	 * @param xpathExpr
	 *            The XPath expression.
	 * 
	 * @throws JaxenException
	 *             if there is a syntax error while parsing the expression
	 */
	public AnythingXPath(String xpathExpr) throws JaxenException {
		super(xpathExpr, AnythingDocumentNavigator.getInstance());
	}

	@SuppressWarnings("unchecked")
	public static Any[] access(ImmutableAny any, String xpath) throws NullPointerException, IllegalArgumentException {
		Any[] result = new Any[0];
		try {
			XPath path = new AnythingXPath(xpath);
			List<AnythingElement> results = path.selectNodes(any);
			result = new Any[results.size()];
			for (int i = 0; i < result.length; i++) {
				result[i] = (Any)results.get(i).getAny();				
			}
		} catch (JaxenException e) {
			throw new IllegalArgumentException(e);
		}

		return result;
	}
	
	
	@Override
	protected Context getContext(Object node) {
		if (node instanceof Context) {
			return (Context) node;
		}

		if (node instanceof AnythingElement) {
			return super.getContext(node);
		}

//		if (node instanceof List) {
//			List<?> newList = new ArrayList<Object>();
//
//			for (Iterator<?> listIter = ((List<?>) node).iterator(); listIter.hasNext();) {
//				newList.add(new AnythingElement(null, "root", listIter.next()));
//			}
//
//			return super.getContext(newList);
//		}

		if (node instanceof SequentialAny) {
			return super.getContext(new AnythingElement(null, "root", ((SequentialAny)node).toAny()));			
		}
		
		return super.getContext(new AnythingElement(null, "root", Any.create.any(node)));
	}

	@Override
	public Object evaluate(Object node) throws JaxenException {
		Object result = super.evaluate(node);

		if (result instanceof AnythingElement) {
			return ((AnythingElement)result).getAny();
		} else if (result instanceof Collection<?>) {
			List<Object> newList = new ArrayList<Object>();

			for (Iterator<?> it = ((Collection<?>)result).iterator(); it.hasNext();) {
				Object member = it.next();

				if (member instanceof AnythingElement) {
					newList.add(((AnythingElement)member).getAny());
				} else {
					newList.add(member);
				}
			}

			return newList;
		}

		return result;
	}
	
	public static void main(String[] args) throws JaxenException {
		Any any = create.any("foo1").put("a", create.any("foo2").put("b", create.any("foo3").put("c", "42").put("a", true)).put("a", false));
		System.out.println(any);
		
		XPath path = new AnythingXPath("a/b/c | a/b/c");
		List<?> results = path.selectNodes(any);
		System.out.println(results);
		
		path = new AnythingXPath("*[2]");
		results = path.selectNodes(any);
		
		System.out.println(results);
		
	}
	
}
